--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
Commit 7ee0171ec7e6179a8d18175321fdcde6800bbe21
Parents : e58c8a9
Author : Mark Qvist <mark@unsigned.io>
Date : 2022-10-02T01:14:29+02:00
Added service split for Android and notifications on Android and Linux
Changes
8 files changed, 336 insertions(+), 103 deletions(-)
Diff
diff --git a/sbapp/main.py b/sbapp/main.py
index 59524b86..38ee2031 100644
--- a/sbapp/main.py
+++ b/sbapp/main.py
@@ -72,8 +72,6 @@ class SidebandApp(MDApp):
self.android_service = None
self.app_dir = plyer.storagepath.get_application_dir()
- # TODO: Remove
- RNS.log("Application directory is: "+str(self.app_dir))
if RNS.vendor.platformutils.get_platform() == "android":
self.sideband = SidebandCore(self, is_client=True, android_app_dir=self.app_dir)
@@ -82,20 +80,16 @@ class SidebandApp(MDApp):
self.conversations_view = None
- self.flag_new_conversations = False
- self.flag_unread_conversations = False
- self.flag_new_announces = False
- self.lxmf_sync_dialog_open = False
self.sync_dialog = None
Window.softinput_mode = "below_target"
self.icon = self.sideband.asset_dir+"/icon.png"
self.notification_icon = self.sideband.asset_dir+"/notification_icon.png"
+ self.check_permissions()
def start_core(self, dt):
self.start_service()
- self.open_conversations()
Clock.schedule_interval(self.jobs, 1)
def dismiss_splash(dt):
@@ -105,8 +99,15 @@ class SidebandApp(MDApp):
if RNS.vendor.platformutils.get_platform() == "android":
Clock.schedule_once(dismiss_splash, 0)
- if self.sideband.first_run:
- self.guide_action()
+ self.sideband.setstate("app.loaded", True)
+ self.sideband.setstate("app.running", True)
+ self.sideband.setstate("app.foreground", True)
+
+ if self.sideband.first_run:
+ self.guide_action()
+ self.request_notifications_permission()
+ else:
+ self.open_conversations()
self.app_state = SidebandApp.ACTIVE
@@ -125,8 +126,6 @@ class SidebandApp(MDApp):
time.sleep(7)
# Start local core instance
- # TODO: Remove log
- RNS.log("Starting local core")
self.sideband.start()
else:
@@ -150,14 +149,20 @@ class SidebandApp(MDApp):
mActivity.startActivity(shareIntent)
def on_pause(self):
+ self.sideband.setstate("app.running", True)
+ self.sideband.setstate("app.foreground", False)
self.app_state = SidebandApp.PAUSED
self.sideband.should_persist_data()
return True
def on_resume(self):
+ self.sideband.setstate("app.running", True)
+ self.sideband.setstate("app.foreground", True)
self.app_state = SidebandApp.ACTIVE
def on_stop(self):
+ self.sideband.setstate("app.running", False)
+ self.sideband.setstate("app.foreground", False)
self.app_state = SidebandApp.STOPPING
def is_in_foreground(self):
@@ -166,25 +171,27 @@ class SidebandApp(MDApp):
else:
return False
- def notify(self, title, content):
- notifications_enabled = True
-
- if notifications_enabled:
- if RNS.vendor.platformutils.get_platform() == "android":
- notifications_permitted = False
- if check_permission("android.permission.POST_NOTIFICATIONS"):
- notifications_permitted = True
- else:
- RNS.log("Requesting notification permission")
- request_permissions(["android.permission.POST_NOTIFICATIONS"])
+ def check_permissions(self):
+ if RNS.vendor.platformutils.get_platform() == "android":
+ if check_permission("android.permission.POST_NOTIFICATIONS"):
+ RNS.log("Have notification permissions")
+ self.sideband.setpersistent("permissions.notifications", True)
else:
- notifications_permitted = True
+ RNS.log("Do not have notification permissions")
+ self.sideband.setpersistent("permissions.notifications", False)
+ else:
+ self.sideband.setpersistent("permissions.notifications", True)
- if notifications_permitted:
- if RNS.vendor.platformutils.get_platform() == "android":
- plyer.notification.notify(title, content, notification_icon=self.notification_icon)
- else:
- plyer.notification.notify(title, content)
+ def request_permissions(self):
+ self.request_notifications_permission()
+
+ def request_notifications_permission(self):
+ if RNS.vendor.platformutils.get_platform() == "android":
+ if not check_permission("android.permission.POST_NOTIFICATIONS"):
+ RNS.log("Requesting notification permission")
+ request_permissions(["android.permission.POST_NOTIFICATIONS"])
+
+ self.check_permissions()
def build(self):
FONT_PATH = self.sideband.asset_dir+"/fonts"
@@ -201,22 +208,22 @@ class SidebandApp(MDApp):
self.message_area_detect()
elif self.root.ids.screen_manager.current == "conversations_screen":
- if self.flag_new_conversations:
+ if self.sideband.getstate("app.flags.new_conversations"):
RNS.log("Updating because of new conversations flag")
if self.conversations_view != None:
self.conversations_view.update()
- if self.flag_unread_conversations:
+ if self.sideband.getstate("app.flags.unread_conversations"):
RNS.log("Updating because of unread messages flag")
if self.conversations_view != None:
self.conversations_view.update()
- if self.lxmf_sync_dialog_open and self.sync_dialog != None:
+ if self.sideband.getstate("app.flags.lxmf_sync_dialog_open") and self.sync_dialog != None:
self.sync_dialog.ids.sync_progress.value = self.sideband.get_sync_progress()*100
self.sync_dialog.ids.sync_status.text = self.sideband.get_sync_status()
elif self.root.ids.screen_manager.current == "announces_screen":
- if self.flag_new_announces:
+ if self.sideband.getstate("app.flags.new_announces"):
RNS.log("Updating because of new announces flag")
if self.announces_view != None:
self.announces_view.update()
@@ -304,6 +311,9 @@ class SidebandApp(MDApp):
self.root.ids.nav_drawer.set_state("closed")
self.sideband.should_persist_data()
+ self.sideband.setstate("app.running", False)
+ self.sideband.setstate("app.foreground", False)
+
def final_exit(dt):
RNS.exit()
MDApp.get_running_app().stop()
@@ -328,13 +338,6 @@ class SidebandApp(MDApp):
yes_button.bind(on_release=dl_yes)
dialog.open()
- def conversation_update(self, context_dest):
- pass
- # if self.root.ids.messages_scrollview.active_conversation == context_dest:
- # self.messages_view.update_widget()
- # else:
- # RNS.log("Not updating since context_dest does not match active")
-
#################################################
# Screens #
@@ -372,13 +375,15 @@ class SidebandApp(MDApp):
self.root.ids.messages_scrollview.scroll_y = 0
self.root.ids.messages_toolbar.title = self.sideband.peer_display_name(context_dest)
self.root.ids.messages_scrollview.active_conversation = context_dest
+ self.sideband.setstate("app.active_conversation", context_dest)
self.root.ids.nokeys_text.text = ""
self.message_area_detect()
self.update_message_widgets()
self.root.ids.screen_manager.current = "messages_screen"
-
+ self.sideband.setstate("app.displaying", self.root.ids.screen_manager.current)
+
self.sideband.read_conversation(context_dest)
def close_messages_action(self, sender=None):
@@ -480,6 +485,7 @@ class SidebandApp(MDApp):
self.root.ids.screen_manager.current = "conversations_screen"
self.root.ids.messages_scrollview.active_conversation = None
+ self.sideband.setstate("app.displaying", self.root.ids.screen_manager.current)
def connectivity_status(self, sender):
hs = dp(22)
@@ -567,18 +573,12 @@ class SidebandApp(MDApp):
)
dialog.d_content = dialog_content
def dl_close(s):
- self.lxmf_sync_dialog_open = False
+ self.sideband.setstate("app.flags.lxmf_sync_dialog_open", False)
dialog.dismiss()
self.sideband.cancel_lxmf_sync()
- # def dl_stop(s):
- # self.lxmf_sync_dialog_open = False
- # dialog.dismiss()
- # self.sideband.cancel_lxmf_sync()
-
close_button.bind(on_release=dl_close)
- # stop_button.bind(on_release=dl_stop)
- self.lxmf_sync_dialog_open = True
+ self.sideband.setstate("app.flags.lxmf_sync_dialog_open", True)
self.sync_dialog = dialog_content
dialog.open()
dialog_content.ids.sync_progress.value = self.sideband.get_sync_progress()*100
@@ -652,6 +652,7 @@ class SidebandApp(MDApp):
self.root.ids.screen_manager.transition.direction = "left"
self.root.ids.screen_manager.current = "information_screen"
self.root.ids.nav_drawer.set_state("closed")
+ self.sideband.setstate("app.displaying", self.root.ids.screen_manager.current)
def close_information_action(self, sender=None):
self.open_conversations(direction="right")
@@ -743,6 +744,7 @@ class SidebandApp(MDApp):
self.root.ids.screen_manager.current = "settings_screen"
self.root.ids.nav_drawer.set_state("closed")
+ self.sideband.setstate("app.displaying", self.root.ids.screen_manager.current)
def close_settings_action(self, sender=None):
self.open_conversations(direction="right")
@@ -852,6 +854,7 @@ class SidebandApp(MDApp):
self.root.ids.screen_manager.transition.direction = "left"
self.root.ids.screen_manager.current = "connectivity_screen"
self.root.ids.nav_drawer.set_state("closed")
+ self.sideband.setstate("app.displaying", self.root.ids.screen_manager.current)
def close_connectivity_action(self, sender=None):
@@ -875,6 +878,7 @@ class SidebandApp(MDApp):
self.root.ids.announces_scrollview.add_widget(self.announces_view.get_widget())
self.root.ids.screen_manager.current = "announces_screen"
+ self.sideband.setstate("app.displaying", self.root.ids.screen_manager.current)
def close_announces_action(self, sender=None):
self.open_conversations(direction="right")
@@ -900,6 +904,7 @@ class SidebandApp(MDApp):
self.root.ids.screen_manager.transition.direction = "left"
self.root.ids.screen_manager.current = "keys_screen"
self.root.ids.nav_drawer.set_state("closed")
+ self.sideband.setstate("app.displaying", self.root.ids.screen_manager.current)
def close_keys_action(self, sender=None):
self.open_conversations(direction="right")
@@ -1031,6 +1036,7 @@ Thank you very much for using Free Communications Systems.
self.root.ids.screen_manager.transition.direction = "left"
self.root.ids.screen_manager.current = "guide_screen"
self.root.ids.nav_drawer.set_state("closed")
+ self.sideband.setstate("app.displaying", self.root.ids.screen_manager.current)
#################################################
@@ -1049,6 +1055,7 @@ Thank you very much for using Free Communications Systems.
self.root.ids.screen_manager.transition.direction = "left"
self.root.ids.screen_manager.current = "map_screen"
self.root.ids.nav_drawer.set_state("closed")
+ self.sideband.setstate("app.displaying", self.root.ids.screen_manager.current)
def broadcasts_action(self, sender=None):
def link_exec(sender=None, event=None):
@@ -1062,6 +1069,7 @@ Thank you very much for using Free Communications Systems.
self.root.ids.screen_manager.transition.direction = "left"
self.root.ids.screen_manager.current = "broadcasts_screen"
self.root.ids.nav_drawer.set_state("closed")
+ self.sideband.setstate("app.displaying", self.root.ids.screen_manager.current)
diff --git a/sbapp/patches/PythonService.java b/sbapp/patches/PythonService.java
index 41b59b38..a912f9a3 100644
--- a/sbapp/patches/PythonService.java
+++ b/sbapp/patches/PythonService.java
@@ -130,20 +130,22 @@ public class PythonService extends Service implements Runnable {
} else {
// for android 8+ we need to create our own channel
// https://stackoverflow.com/questions/47531742/startforeground-fail-after-upgrade-to-android-8-1
- String NOTIFICATION_CHANNEL_ID = "org.kivy.p4a"; //TODO: make this configurable
- String channelName = "Background Service"; //TODO: make this configurable
+ String NOTIFICATION_CHANNEL_ID = "io.unsigned.sideband.reticulum";
+ String channelName = "Background Service";
NotificationChannel chan = new NotificationChannel(NOTIFICATION_CHANNEL_ID, channelName,
NotificationManager.IMPORTANCE_NONE);
chan.setLightColor(Color.BLUE);
- chan.setLockscreenVisibility(Notification.VISIBILITY_PRIVATE);
+ chan.setLockscreenVisibility(Notification.VISIBILITY_SECRET);
+ chan.setShowBadge(false);
NotificationManager manager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
manager.createNotificationChannel(chan);
Notification.Builder builder = new Notification.Builder(context, NOTIFICATION_CHANNEL_ID);
- builder.setContentTitle("Sideband");
- builder.setContentText("Reticulum Running");
+ builder.setContentTitle("Sideband Active");
+ // builder.setContentText("Reticulum Active");
builder.setContentIntent(pIntent);
+ // builder.setOngoing(true);
// TODO: Generalise this
Bitmap icon_bitmap = BitmapFactory.decodeFile("/data/user/0/io.unsigned.sideband/files/app/assets/notification_icon.png");
diff --git a/sbapp/plyer/facades/notification.py b/sbapp/plyer/facades/notification.py
index 5b09dee1..71c7d532 100644
--- a/sbapp/plyer/facades/notification.py
+++ b/sbapp/plyer/facades/notification.py
@@ -46,7 +46,7 @@ class Notification:
'''
def notify(self, title='', message='', app_name='', app_icon='', notification_icon=None,
- timeout=10, ticker='', toast=False, hints={}):
+ timeout=10, ticker='', toast=False, hints={}, context_override=None):
'''
Send a notification.
@@ -84,7 +84,7 @@ class Notification:
self._notify(
title=title, message=message,
app_icon=app_icon, app_name=app_name, notification_icon=notification_icon,
- timeout=timeout, ticker=ticker, toast=toast, hints=hints
+ timeout=timeout, ticker=ticker, toast=toast, hints=hints, context_override=context_override
)
# private
diff --git a/sbapp/plyer/platforms/android/notification.py b/sbapp/plyer/platforms/android/notification.py
index 24a567da..ac9f76b7 100644
--- a/sbapp/plyer/platforms/android/notification.py
+++ b/sbapp/plyer/platforms/android/notification.py
@@ -180,6 +180,7 @@ class AndroidNotification(Notification):
)
icon = kwargs.get('app_icon')
notification_icon = kwargs.get('notification_icon')
+ context_override = kwargs.get('context_override')
# decide whether toast only or proper notification
if kwargs.get('toast'):
diff --git a/sbapp/services/sidebandservice.py b/sbapp/services/sidebandservice.py
index 89e87d53..53aa0feb 100644
--- a/sbapp/services/sidebandservice.py
+++ b/sbapp/services/sidebandservice.py
@@ -9,7 +9,18 @@ Logger.setLevel(LOG_LEVELS["debug"])
if RNS.vendor.platformutils.get_platform() == "android":
from jnius import autoclass, cast
+ from android import python_act
Context = autoclass('android.content.Context')
+ Intent = autoclass('android.content.Intent')
+ BitmapFactory = autoclass('android.graphics.BitmapFactory')
+ Icon = autoclass("android.graphics.drawable.Icon")
+ PendingIntent = autoclass('android.app.PendingIntent')
+ AndroidString = autoclass('java.lang.String')
+ NotificationManager = autoclass('android.app.NotificationManager')
+ Context = autoclass('android.content.Context')
+ NotificationBuilder = autoclass('android.app.Notification$Builder')
+ NotificationChannel = autoclass('android.app.NotificationChannel')
+
from sideband.core import SidebandCore
else:
@@ -20,6 +31,64 @@ class AppProxy():
pass
class SidebandService():
+ def android_notification(self, title="", content="", ticker="", group=None, context_id=None):
+ package_name = "io.unsigned.sideband"
+
+ if not self.notification_service:
+ self.notification_service = cast(NotificationManager, self.app_context.getSystemService(
+ Context.NOTIFICATION_SERVICE
+ ))
+
+ channel_id = package_name
+ group_id = ""
+ if group != None:
+ channel_id += "."+str(group)
+ group_id += str(group)
+ if context_id != None:
+ channel_id += "."+str(context_id)
+ group_id += "."+str(context_id)
+
+ if not title or title == "":
+ channel_name = "Sideband"
+ else:
+ channel_name = title
+
+ self.notification_channel = NotificationChannel(channel_id, channel_name, NotificationManager.IMPORTANCE_DEFAULT)
+ self.notification_channel.enableVibration(True)
+ self.notification_channel.setShowBadge(True)
+ self.notification_service.createNotificationChannel(self.notification_channel)
+
+ notification = NotificationBuilder(self.app_context, channel_id)
+ notification.setContentTitle(title)
+ notification.setContentText(AndroidString(content))
+
+ # if group != None:
+ # notification.setGroup(group_id)
+
+ if not self.notification_small_icon:
+ path = self.sideband.notification_icon
+ bitmap = BitmapFactory.decodeFile(path)
+ self.notification_small_icon = Icon.createWithBitmap(bitmap)
+
+ notification.setSmallIcon(self.notification_small_icon)
+
+ # large_icon_path = self.sideband.icon
+ # bitmap_icon = BitmapFactory.decodeFile(large_icon_path)
+ # notification.setLargeIcon(bitmap_icon)
+
+ if not self.notification_intent:
+ notification_intent = Intent(self.app_context, python_act)
+ notification_intent.setFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP)
+ notification_intent.setAction(Intent.ACTION_MAIN)
+ notification_intent.addCategory(Intent.CATEGORY_LAUNCHER)
+
+ self.notification_intent = PendingIntent.getActivity(self.app_context, 0, notification_intent, 0)
+
+ notification.setContentIntent(self.notification_intent)
+ notification.setAutoCancel(True)
+
+ built_notification = notification.build()
+ self.notification_service.notify(0, built_notification)
def __init__(self):
self.argument = environ.get('PYTHON_SERVICE_ARGUMENT', '')
@@ -34,15 +103,20 @@ class SidebandService():
self.app_context = None
self.wifi_manager = None
+ self.notification_service = None
+ self.notification_channel = None
+ self.notification_intent = None
+ self.notification_small_icon = None
+
if RNS.vendor.platformutils.get_platform() == "android":
self.android_service = autoclass('org.kivy.android.PythonService').mService
self.app_context = self.android_service.getApplication().getApplicationContext()
self.wifi_manager = self.app_context.getSystemService(Context.WIFI_SERVICE)
- # The returned instance is an android.net.wifi.WifiManager
+ # The returned instance /\ is an android.net.wifi.WifiManager
- # TODO: Remove?
- RNS.log("Sideband background service created, starting core...", RNS.LOG_DEBUG)
self.sideband = SidebandCore(self.app_proxy, is_service=True, android_app_dir=self.app_dir)
+ self.sideband.service_context = self.android_service
+ self.sideband.owner_service = self
self.sideband.start()
def start(self):
@@ -76,8 +150,4 @@ class SidebandService():
self.release_locks()
sbs = SidebandService()
-sbs.start()
-
-# TODO: Remove
-print("SBS: Service thread done")
-RNS.log("Service thread done")
\ No newline at end of file
+sbs.start()
\ No newline at end of file
diff --git a/sbapp/sideband/core.py b/sbapp/sideband/core.py
index eed94605..f36eae14 100644
--- a/sbapp/sideband/core.py
+++ b/sbapp/sideband/core.py
@@ -8,6 +8,9 @@ import sqlite3
import RNS.vendor.umsgpack as msgpack
+if RNS.vendor.platformutils.get_platform() == "android":
+ from jnius import autoclass, cast
+
class PropagationNodeDetector():
EMITTED_DELTA_GRACE = 300
EMITTED_DELTA_IGNORE = 10
@@ -60,6 +63,8 @@ class SidebandCore():
MAX_ANNOUNCES = 48
+ JOB_INTERVAL = 1
+
aspect_filter = "lxmf.delivery"
def received_announce(self, destination_hash, announced_identity, app_data):
# Add the announce to the directory announce
@@ -75,12 +80,6 @@ class SidebandCore():
else:
self.is_standalone = False
- if self.is_client:
- from .serviceproxy import ServiceProxy
- self.serviceproxy = ServiceProxy(self)
- else:
- self.serviceproxy = None
-
self.owner_app = owner_app
self.reticulum = None
@@ -90,16 +89,23 @@ class SidebandCore():
if RNS.vendor.platformutils.get_platform() == "android":
self.app_dir = android_app_dir+"/io.unsigned.sideband/files/"
self.rns_configdir = self.app_dir+"/app_storage/reticulum"
+ self.asset_dir = self.app_dir+"/app/assets"
+ else:
+ self.asset_dir = plyer.storagepath.get_application_dir()+"/sbapp/assets"
+
+ self.icon = self.asset_dir+"/icon.png"
+ self.icon_48 = self.asset_dir+"/icon_48.png"
+ self.icon_32 = self.asset_dir+"/icon_32.png"
+ self.notification_icon = self.asset_dir+"/notification_icon.png"
if not os.path.isdir(self.app_dir+"/app_storage"):
os.makedirs(self.app_dir+"/app_storage")
- self.asset_dir = self.app_dir+"/app/assets"
self.config_path = self.app_dir+"/app_storage/sideband_config"
self.identity_path = self.app_dir+"/app_storage/primary_identity"
self.db_path = self.app_dir+"/app_storage/sideband.db"
self.lxmf_storage = self.app_dir+"/app_storage/"
-
+
self.first_run = True
try:
@@ -195,6 +201,9 @@ class SidebandCore():
if not os.path.isfile(self.db_path):
self.__db_init()
+ else:
+ self._db_initstate()
+ self._db_initpersistent()
def __save_config(self):
@@ -222,11 +231,32 @@ class SidebandCore():
RNS.log("Error while setting LXMF propagation node: "+str(e), RNS.LOG_ERROR)
+ def notify(self, title, content, group=None, context_id=None):
+ notifications_enabled = True
+
+ if notifications_enabled:
+ if RNS.vendor.platformutils.get_platform() == "android":
+ if self.getpersistent("permissions.notifications"):
+ notifications_permitted = True
+ else:
+ notifications_permitted = False
+ else:
+ notifications_permitted = True
+
+ if notifications_permitted:
+ if RNS.vendor.platformutils.get_platform() == "android":
+ if self.is_service:
+ self.owner_service.android_notification(title, content, group=group, context_id=context_id)
+ else:
+ plyer.notification.notify(title, content, notification_icon=self.notification_icon, context_override=None)
+ else:
+ plyer.notification.notify(title, content, app_icon=self.icon_32)
+
def log_announce(self, dest, app_data, dest_type):
try:
RNS.log("Received "+str(dest_type)+" announce for "+RNS.prettyhexrep(dest)+" with data: "+app_data.decode("utf-8"))
self._db_save_announce(dest, app_data, dest_type)
- self.owner_app.flag_new_announces = True
+ self.setstate("app.flags.new_announces", True)
except Exception as e:
RNS.log("Exception while decoding LXMF destination announce data:"+str(e))
@@ -345,6 +375,27 @@ class SidebandCore():
else:
return []
+ def gui_foreground(self):
+ return self._db_getstate("app.foreground")
+
+ def gui_display(self):
+ return self._db_getstate("app.displaying")
+
+ def gui_conversation(self):
+ return self._db_getstate("app.active_conversation")
+
+ def setstate(self, prop, val):
+ self._db_setstate(prop, val)
+
+ def getstate(self, prop):
+ return self._db_getstate(prop)
+
+ def setpersistent(self, prop, val):
+ self._db_setpersistent(prop, val)
+
+ def getpersistent(self, prop):
+ return self._db_getpersistent(prop)
+
def __event_conversations_changed(self):
pass
@@ -364,6 +415,100 @@ class SidebandCore():
dbc.execute("DROP TABLE IF EXISTS announce")
dbc.execute("CREATE TABLE announce (id PRIMARY KEY, received INTEGER, source BLOB, data BLOB, dest_type BLOB)")
+ dbc.execute("DROP TABLE IF EXISTS state")
+ dbc.execute("CREATE TABLE state (property BLOB PRIMARY KEY, value BLOB)")
+
+ dbc.execute("DROP TABLE IF EXISTS persistent")
+ dbc.execute("CREATE TABLE persistent (property BLOB PRIMARY KEY, value BLOB)")
+
+ db.commit()
+ db.close()
+
+ def _db_initstate(self):
+ db = sqlite3.connect(self.db_path)
+ dbc = db.cursor()
+
+ dbc.execute("DROP TABLE IF EXISTS state")
+ dbc.execute("CREATE TABLE state (property BLOB PRIMARY KEY, value BLOB)")
+ self._db_setstate("database_ready", True)
+
+ def _db_getstate(self, prop):
+ db = sqlite3.connect(self.db_path)
+ dbc = db.cursor()
+
+ query = "select * from state where property=:uprop"
+ dbc.execute(query, {"uprop": prop.encode("utf-8")})
+ result = dbc.fetchall()
+ db.close()
+
+ if len(result) < 1:
+ return None
+ else:
+ try:
+ entry = result[0]
+ val = msgpack.unpackb(entry[1])
+ return val
+ except Exception as e:
+ RNS.log("Could not unpack state value from database for property \""+str(prop)+"\". The contained exception was: "+str(e), RNS.LOG_ERROR)
+ return None
+
+ def _db_setstate(self, prop, val):
+ db = sqlite3.connect(self.db_path)
+ dbc = db.cursor()
+ uprop = prop.encode("utf-8")
+ bval = msgpack.packb(val)
+
+ if self._db_getstate(prop) == None:
+ query = "INSERT INTO state (property, value) values (?, ?)"
+ data = (uprop, bval)
+ dbc.execute(query, data)
+ else:
+ query = "UPDATE state set value=:bval where property=:uprop;"
+ dbc.execute(query, {"bval": bval, "uprop": uprop})
+
+ db.commit()
+ db.close()
+
+ def _db_initpersistent(self):
+ db = sqlite3.connect(self.db_path)
+ dbc = db.cursor()
+
+ dbc.execute("CREATE TABLE IF NOT EXISTS persistent (property BLOB PRIMARY KEY, value BLOB)")
+
+ def _db_getpersistent(self, prop):
+ db = sqlite3.connect(self.db_path)
+ dbc = db.cursor()
+
+ query = "select * from persistent where property=:uprop"
+ dbc.execute(query, {"uprop": prop.encode("utf-8")})
+ result = dbc.fetchall()
+ db.close()
+
+ if len(result) < 1:
+ return None
+ else:
+ try:
+ entry = result[0]
+ val = msgpack.unpackb(entry[1])
+ return val
+ except Exception as e:
+ RNS.log("Could not unpack persistent value from database for property \""+str(prop)+"\". The contained exception was: "+str(e), RNS.LOG_ERROR)
+ return None
+
+ def _db_setpersistent(self, prop, val):
+ db = sqlite3.connect(self.db_path)
+ dbc = db.cursor()
+ uprop = prop.encode("utf-8")
+ bval = msgpack.packb(val)
+
+ if self._db_getpersistent(prop) == None:
+ query = "INSERT INTO persistent (property, value) values (?, ?)"
+ data = (uprop, bval)
+ dbc.execute(query, data)
+ else:
+ query = "UPDATE persistent set value=:bval where property=:uprop;"
+ dbc.execute(query, {"bval": bval, "uprop": uprop})
+
db.commit()
db.close()
@@ -687,7 +832,11 @@ class SidebandCore():
db.close()
def lxmf_announce(self):
- self.lxmf_destination.announce()
+ if self.is_standalone or self.is_service:
+ self.lxmf_destination.announce()
+ self.setstate("wants.announce", False)
+ else:
+ self.setstate("wants.announce", True)
def is_known(self, dest_hash):
try:
@@ -710,11 +859,21 @@ class SidebandCore():
RNS.log("Error while querying for key: "+str(e), RNS.LOG_ERROR)
return False
+ def _service_jobs(self):
+ if self.is_service:
+ while True:
+ time.sleep(SidebandCore.JOB_INTERVAL)
+ if self.getstate("wants.announce"):
+ self.lxmf_announce()
+
def __start_jobs_deferred(self):
if self.config["start_announce"]:
- # TODO: (Service) Handle this in service
- self.lxmf_destination.announce()
+ self.lxmf_announce()
+ if self.is_service:
+ self.service_thread = threading.Thread(target=self._service_jobs, daemon=True)
+ self.service_thread.start()
+
def __start_jobs_immediate(self):
# TODO: Reset loglevel
self.reticulum = RNS.Reticulum(configdir=self.rns_configdir, loglevel=7)
@@ -828,19 +987,14 @@ class SidebandCore():
RNS.log("Error while adding I2P Interface. The contained exception was: "+str(e))
self.interface_i2p = None
- if self.is_service or self.is_standalone:
- RNS.log("Reticulum started, activating LXMF...")
- self.message_router = LXMF.LXMRouter(identity = self.identity, storagepath = self.lxmf_storage, autopeer = True)
- self.message_router.register_delivery_callback(self.lxmf_delivery)
-
- self.lxmf_destination = self.message_router.register_delivery_identity(self.identity, display_name=self.config["display_name"])
- self.lxmf_destination.set_default_app_data(self.get_display_name_bytes)
+ RNS.log("Reticulum started, activating LXMF...")
+ self.message_router = LXMF.LXMRouter(identity = self.identity, storagepath = self.lxmf_storage, autopeer = True)
+ self.message_router.register_delivery_callback(self.lxmf_delivery)
- self.rns_dir = RNS.Reticulum.configdir
+ self.lxmf_destination = self.message_router.register_delivery_identity(self.identity, display_name=self.config["display_name"])
+ self.lxmf_destination.set_default_app_data(self.get_display_name_bytes)
- else:
- self.message_router = self.serviceproxy
- self.lxmf_destination = self.serviceproxy
+ self.rns_dir = RNS.Reticulum.configdir
if self.config["lxmf_propagation_node"] != None and self.config["lxmf_propagation_node"] != "":
self.set_active_propagation_node(self.config["lxmf_propagation_node"])
@@ -941,23 +1095,21 @@ class SidebandCore():
if self._db_conversation(context_dest) == None:
self._db_create_conversation(context_dest)
- self.owner_app.flag_new_conversations = True
+ self.setstate("app.flags.new_conversations", True)
- if self.owner_app.root.ids.screen_manager.current == "messages_screen":
- if self.owner_app.root.ids.messages_scrollview.active_conversation != context_dest:
+ if self.gui_display() == "messages_screen":
+ if self.gui_conversation() != context_dest:
self.unread_conversation(context_dest)
- self.owner_app.flag_unread_conversations = True
+ self.setstate("app.flags.unread_conversations", True)
else:
- if self.owner_app.is_in_foreground():
+ if self.gui_foreground():
should_notify = False
else:
self.unread_conversation(context_dest)
- self.owner_app.flag_unread_conversations = True
+ self.setstate("app.flags.unread_conversations", True)
- try:
- self.owner_app.conversation_update(context_dest)
- except Exception as e:
- RNS.log("Error in conversation update callback: "+str(e))
+ if self.is_client:
+ should_notify = False
if should_notify:
nlen = 128
@@ -966,7 +1118,7 @@ class SidebandCore():
if len(text) > nlen:
text += "..."
- self.owner_app.notify(title="Message from "+self.peer_display_name(context_dest), content=notification_content)
+ self.notify(title=self.peer_display_name(context_dest), content=notification_content, group="LXM", context_id=RNS.hexrep(context_dest, delimit=False))
def start(self):
@@ -976,6 +1128,8 @@ class SidebandCore():
thread = threading.Thread(target=self.__start_jobs_deferred)
thread.setDaemon(True)
thread.start()
+
+ self._db_setstate("core.started", True)
RNS.log("Sideband Core "+str(self)+" started")
def request_lxmf_sync(self, limit = None):
diff --git a/sbapp/ui/announces.py b/sbapp/ui/announces.py
index 4e59cb00..44024a36 100644
--- a/sbapp/ui/announces.py
+++ b/sbapp/ui/announces.py
@@ -39,10 +39,8 @@ class Announces():
def update(self):
self.clear_list()
self.announces = self.app.sideband.list_announces()
-
self.update_widget()
-
- self.app.flag_new_announces = False
+ self.app.sideband.setstate("app.flags.new_announces", False)
def update_widget(self):
if self.list == None:
diff --git a/sbapp/ui/conversations.py b/sbapp/ui/conversations.py
index 0e035b9a..30a4c942 100644
--- a/sbapp/ui/conversations.py
+++ b/sbapp/ui/conversations.py
@@ -45,14 +45,14 @@ class Conversations():
self.added_item_dests = []
def update(self):
- if self.app.flag_unread_conversations:
+ if self.app.sideband.getstate("app.flags.unread_conversations"):
self.clear_list()
self.context_dests = self.app.sideband.list_conversations()
self.update_widget()
- self.app.flag_new_conversations = False
- self.app.flag_unread_conversations = False
+ self.app.sideband.setstate("app.flags.unread_conversations", False)
+ self.app.sideband.setstate("app.flags.new_conversations", False)
def update_widget(self):
if self.list == None:
──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────